package utils

import (
	"fmt"
	"io"
	"sync/atomic"
	"time"
)

type IOWrapper struct {
	disFlag    int32
	closedFlag int32
	disReason  string

	recvBlock []byte

	reconnectInterval time.Duration

	connecttime time.Time

	connstr string
	argsMap StrMap
	conn    io.ReadWriteCloser

	OpenFunc   func(argsMap StrMap) (conn io.ReadWriteCloser, err error)
	OnWroteBuf func(sender *IOWrapper, buf []byte)
	OnReadBuf  func(sender *IOWrapper, buf []byte)
}

func NewIOWrapper() *IOWrapper {
	return &IOWrapper{reconnectInterval: time.Second}
}

func (this *IOWrapper) doReadBuf(buf []byte) {
	evt := this.OnReadBuf
	if evt != nil {
		evt(this, buf)
	}
}

func (this *IOWrapper) doWroteBuf(buf []byte) {
	evt := this.OnWroteBuf
	if evt != nil {
		evt(this, buf)
	}
}

func (this *IOWrapper) RequestDis(reason string) {
	conn := this.conn
	if atomic.CompareAndSwapInt32(&this.disFlag, 0, 1) {
		this.disReason = reason
		if conn != nil {
			conn.Close()
		}
	}
}

func (this *IOWrapper) Close() error {
	conn := this.conn
	if conn != nil {
		this.conn = nil
		return conn.Close()
	}
	return nil
}

func (this *IOWrapper) SetConnStr(connstr string) (isChanged bool) {
	if this.connstr != connstr {
		this.connstr = connstr
		strmap := NewStrMap0()
		strmap.URLFormDecode(connstr)
		this.argsMap = strmap
		this.RequestDis("配置更新")
		return true
	}
	return false
}

func (this *IOWrapper) CheckConnect() error {
	if this.conn != nil {
		return nil
	}

	fn := this.OpenFunc
	if fn == nil {
		return fmt.Errorf("缺少连接事件函数(OpenFunc)")
	}
	strMap := this.argsMap
	if strMap == nil {
		return fmt.Errorf("缺少连接参数配置")
	}
	conn, err := fn(strMap)
	if err != nil {
		return err
	}
	this.conn = conn
	atomic.StoreInt32(&this.disFlag, 0)
	this.reconnectInterval = time.Duration(strMap.IntByName("reconnectInterval", 1)) * time.Second
	l := strMap.IntByName("block", 1024)
	if l <= 0 {
		l = 1024
	}
	if len(this.recvBlock) != l {
		this.recvBlock = make([]byte, l)
	}
	return nil
}

func (this *IOWrapper) CheckWrite(buf []byte) error {
	err := this.CheckConnect()
	if err != nil {
		return err
	}
	_, err = this.Write(buf)
	if err != nil {
		this.RequestDis(err.Error())
	}
	return err
}

func (this *IOWrapper) CheckRead() (buf []byte, err error) {
	err = this.CheckConnect()
	if err != nil {
		return nil, err
	}

	recvBlock := this.recvBlock
	n, err := this.Read(recvBlock)
	if err != nil {
		this.RequestDis(err.Error())
		return nil, err
	}
	return recvBlock[:n], nil

}

func (this *IOWrapper) CheckReadEx() (buf []byte, err error) {
	err = this.CheckConnect()
	if err != nil {
		return nil, err
	}

	recvBlock := this.recvBlock
	n, err := this.Read(recvBlock)
	if n > 0 {
		return recvBlock[:n], err
	} else {
		return nil, err
	}
}

func (this *IOWrapper) Write(buf []byte) (n int, err error) {
	conn := this.conn
	if conn == nil {
		return -1, io.ErrClosedPipe
	}
	n, err = conn.Write(buf)
	if err == nil {
		this.doWroteBuf(buf[:n])
	}
	return
}

func (this *IOWrapper) Read(buf []byte) (n int, err error) {
	conn := this.conn
	if conn == nil {
		return -1, io.ErrClosedPipe
	}
	n, err = conn.Read(buf)
	if err == nil {
		this.doReadBuf(buf[:n])
	}
	return
}
